import {getUploadKey, sheetUpload as upload, reset, cancel} from "../api/common/upload.js";

export let sheetUpload = {
    /**
     * 启动
     *
     * @param file
     * @param byte
     * @returns {Promise<{msg: string, code: number, data: {file: {fail: [], file: *[]}, key: *}}|{msg: *, code: number}>}
     */
    boot: async function (file, byte = 2097152) {
        // 分片总数
        let totalSheet = Math.ceil(file.size / byte);

        try {
            // 获取上传 key
            let key = await this.getKey({
                name: file.name,
                size: file.size,
                totalSheet: totalSheet
            });

            // 文件分片
            let fileList = await this.sheet(file, totalSheet, byte, key);

            // 分片上传
            let result = await this.upload(fileList);

            return {code: 200, msg: '', data: {key, file: {fail: this.checkFileList(result), file: result}}};
        } catch (e) {
            return {code: 500, msg: e.message};
        }
    },

    /**
     * 文件分片
     *
     * @param file          文件
     * @param totalSheet    总片数
     * @param byte          单片大小
     * @param key           上传key
     * @returns {Promise<[]>}
     */
    sheet: async (file, totalSheet, byte, key) => {
        let startByte = 0, endByte = 1 === totalSheet ? file.size : byte, index = 1, list = [];

        // 初始化变量
        while (startByte < endByte) {
            // 初始化变量
            let fileData = new FormData(), flow = file.slice(startByte, endByte);

            // 生成 md5 签名
            let md5Sign = await new Promise((resolve, reject) => {
                browserMD5File(flow, (err, md5) => resolve(md5));
            });

            fileData.append('block', flow);
            fileData.append('key', key);
            fileData.append('md5', md5Sign.toString());
            fileData.append('index', index.toString());

            // 需上传的文件列表
            list.push(fileData);

            // 切片
            startByte = endByte;
            endByte = startByte + byte > file.size ? file.size : startByte + byte;
            index++;
        }

        return list;
    },

    /**
     * 重新上传失败文件分片
     *
     * @param key   文件 kye
     * @param list  失败文件分片列表
     * @returns {Promise<{msg: string, code: number, data: {file: {fail: *[], file: unknown}, key: *}}|{msg: *, code: number}>}
     */
    resetUpload: async (key, list) => {
        try {
            // 计算所需重置文件下标、文件
            let indexList = [], fileList = [];
            list.forEach(item => {
                indexList.push(item.data.get('index'))
                fileList.push(item.data);
            });

            // 重置文件
            await reset({key, indexList}).then(res => {
                if (200 !== res.code) throw res.msg;
            });

            // 重新分片上传
            let result = await this.upload(fileList);

            return {code: 200, msg: '', data: {key, file: {fail: this.checkFileList(result), file: result}}};
        } catch (e) {
            return {code: 500, msg: e.message};
        }
    },

    /**
     * 取消文件上传
     *
     * @param key
     * @returns {Promise<{}>}
     */
    cancelUpload: async (key) => {
        // 重置文件
        return cancel({key});
    },

    /**
     * 检查文件是否上传失败
     *
     * @param result
     * @returns {[]}
     */
    checkFileList(result) {
        let fail = [];

        result.forEach(item => {
            if (200 !== item.result.code) fail.push(item);
        });

        return fail;
    },

    /**
     * 获取上传 key
     *
     * @param data
     * @returns {*}
     */
    getKey: (data) => {
        return getUploadKey(data).then(res => {
            if (200 !== res.code) throw res.msg;

            return res.data.key;
        });
    },

    /**
     * 上传文件
     *
     * @param list          分片列表
     * @returns {Promise<unknown>}
     */
    upload: (list) => {
        return new Promise((resolve, reject) => {
            let newList = [], index = 0;

            // 上传，全部请求完成则结束
            list.forEach(item => {
                // 上传
                upload(item, {
                    processData: false,
                    contentType: false,
                    headers: {},
                }).then(result => {
                    newList.push({data: item, result});
                }).catch(e => {
                    newList.push({data: item, result: {code: 500, msg: e.message}});
                }).finally(() => {
                    if (list.length === ++index) return resolve(newList);
                });
            });
        })
    }
};
